<!DOCTYPE html><html class="appearance-auto" lang="zh-CN"><head><meta charset="UTF-8"><title>【Spring源码解析系列】 lazy-init预初始化</title><meta name="description" content="用创业者心态做好每一件事情！"><meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, initial-scale=1"><!-- Google Analytics --><!-- End Google Analytics -->
<!-- Baidu Analytics --><!-- End Baidu Analytics --><link rel="icon" href="/blog/images/favicon.png"><link rel="stylesheet" href="/blog/style/common/bulma.css"><link rel="stylesheet" href="/blog/style/base.css"><link rel="stylesheet" href="/blog/style/common/helper.css"><script src="/blog/js/common.js"></script><link rel="stylesheet" href="/blog/style/post.css"><link rel="stylesheet" href="/blog/style/themes/highlight-theme-light.css"><script src="/blog/js/highlight.pack.js"></script><meta name="description" content="
本文主要讲述Spring中lazy-init预初始化


定义​        IOC容器的初始化过程是对Bean定义资源的定位、载入和注册，此时容器对Bean的依赖注入并没有发生，依赖注入主要是应用程序第一次向容器索取Bean时，通过getBean方法的调用完成。​        当Bean定义资源的元素中配置了lazy-init属性时，容器将会在初始化的时候对所配置的Bean进行预实例化，Bean的依赖注入在容器初始化的时候就已经完成。这样，当应用程序第一次向容器索取被管理的Bean时，就不用再初始化和对Bean进行依赖注入了，直接从容器获取已经完成依赖注入的现成Bean，可以提高应用第一次向容器获取Bean的性能。
源码解析AbstractApplicationContext::refresh
//.."><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.1"><link rel="alternate" href="/blog/atom.xml" title="janedler's blog" type="application/atom+xml">
</head><body class="is-flex is-flex-direction-column"><header class="header-widget is-flex-shrink-0 is-hidden-mobile"><div class="container is-fullhd is-flex is-justify-content-space-between is-align-items-center is-full-height"><section class="is-hidden-mobile is-flex-shrink-0"><h2><a href="/blog/">田园牧歌(*︶*)'s blog</a></h2></section><h3 class="is-hidden-mobile is-family-serif is-full-height is-flex is-align-items-center is-flex-shrink-0"><div class="is-full-height" id="postTopic"><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">【Spring源码解析系列】 lazy-init预初始化</p><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">点击返回顶部</p></div></h3><aside class="is-flex-shrink-0"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></aside></div></header><header class="is-flex header-widget is-flex-shrink-0 is-align-items-center is-justify-content-center is-hidden-tablet"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></header><main><main class="container is-max-widescreen content section post-page pt-4 px-4"><div class="columns is-flex-desktop is-justify-content-center is-flex-direction-row-reverse"><div class="column is-3 is-hidden-mobile"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89"><span class="toc-text">定义</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90"><span class="toc-text">源码解析</span></a></li></ol></div><div class="column is-9"><header class="my-4"><a href="/blog/tags/Java"><i class="tag post-item-tag">Java</i></a><a href="/blog/tags/Spring"><i class="tag post-item-tag">Spring</i></a></header><h1 class="mt-0 mb-1 is-family-serif" id="postTitle">【Spring源码解析系列】 lazy-init预初始化</h1><time class="has-text-grey" datetime="2022-11-30T16:00:00.000Z">2022-12-01</time><article class="mt-2 post-content"><p><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/spring.png" alt="cover"></p>
<p>本文主要讲述Spring中lazy-init预初始化</p>
<span id="more"></span>

<h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>​        IOC容器的初始化过程是对Bean定义资源的定位、载入和注册，此时容器对Bean的依赖注入并没有发生，依赖注入主要是应用程序第一次向容器索取Bean时，通过getBean方法的调用完成。<br>​        当Bean定义资源的<Bean>元素中配置了lazy-init属性时，容器将会在初始化的时候对所配置的Bean进行预实例化，Bean的依赖注入在容器初始化的时候就已经完成。这样，当应用程序第一次向容器索取被管理的Bean时，就不用再初始化和对Bean进行依赖注入了，直接从容器获取已经完成依赖注入的现成Bean，可以提高应用第一次向容器获取Bean的性能。</p>
<h2 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h2><p>AbstractApplicationContext::refresh</p>
<pre><code class="java">//容器初始化的过程，读入Bean定义资源，并解析注册
//在创建IOC容器前，如果已经有容器存在，则需要把已有的容器销毁和关闭，以保证在refresh之后使用
//的是新建立起来的IOC容器,refresh的作用类似于对IOC容器的重启，在新建立好容器中对容器进行初始化，
//对Bean定义资源进行载入 IOC容器一定是单例的
@Override
public void refresh() throws BeansException, IllegalStateException &#123;
   synchronized (this.startupShutdownMonitor) &#123;
        ...代码省略...
         //初始化所有剩余的单例Bean
         //对配置了lazy-init属性的Bean进行预实例化处理   
         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);
         ...代码省略...
      &#125;
     ...代码省略...
   &#125;
&#125;
</code></pre>
<p>在AbstractApplicationContext::finishBeanFactoryInitialization中实现对配置了lazy-init属性的Bean进行预实例化处理</p>
<pre><code class="java">//对配置了lazy-init属性的Bean进行预实例化处理
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) &#123;
   // Initialize conversion service for this context.
   //在对某些Bean属性进行转换时使用
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &amp;&amp;
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) &#123;
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   &#125;

   // Register a default embedded value resolver if no BeanFactoryPostProcessor
   // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) &#123;
      beanFactory.addEmbeddedValueResolver(strVal -&gt; getEnvironment().resolvePlaceholders(strVal));
   &#125;

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) &#123;
      getBean(weaverAwareName);
   &#125;
   //为了类型匹配，停止使用临时的类加载器
   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);
   //缓存容器中所有注册的BeanDefinition元数据，以防被修改
   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();
   //对配置了lazy-init属性的单态模式Bean进行实例化处理
   // Instantiate all remaining (non-lazy-init) singletons.
   beanFactory.preInstantiateSingletons();
&#125;
</code></pre>
<p>DefaultListableBeanFacotry对配置lazy-init属性单态Bean的预实例化</p>
<pre><code class="java">//对配置lazy-init属性单态Bean的预实例化
@Override
public void preInstantiateSingletons() throws BeansException &#123;
   if (logger.isTraceEnabled()) &#123;
      logger.trace(&quot;Pre-instantiating singletons in &quot; + this);
   &#125;

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   List&lt;String&gt; beanNames = new ArrayList&lt;&gt;(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   //在对配置lazy-init属性单态Bean的预实例化过程中，必须多线程同步，以确保数据一致性
   for (String beanName : beanNames) &#123;
      //获取指定名称的Bean定义
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      //Bean不是抽象的，是单态模式的，且lazy-init属性配置为false
      if (!bd.isAbstract() &amp;&amp; bd.isSingleton() &amp;&amp; !bd.isLazyInit()) &#123;
         //如果指定名称的bean是创建容器的Bean
         if (isFactoryBean(beanName)) &#123;
            //FACTORY_BEAN_PREFIX=&quot;&amp;&quot;,当Bean名称前面加“&amp;”符号时
            //获取的是产生容器对象本身，而不是容器产生的Bean，
            //调用getBean方法，触发容器对Bean实例化和依赖注入过程
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof SmartFactoryBean&lt;?&gt; smartFactoryBean &amp;&amp; smartFactoryBean.isEagerInit()) &#123;
               getBean(beanName);
            &#125;
         &#125;
         else &#123;
            getBean(beanName);
         &#125;
      &#125;
   &#125;

   // Trigger post-initialization callback for all applicable beans...
   for (String beanName : beanNames) &#123;
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) &#123;
         StartupStep smartInitialize = this.getApplicationStartup().start(&quot;spring.beans.smart-initialize&quot;)
               .tag(&quot;beanName&quot;, beanName);
         smartSingleton.afterSingletonsInstantiated();
         smartInitialize.end();
      &#125;
   &#125;
&#125;
</code></pre>
<p>通过对lazy-init处理源码的分析，我们可以看出，如果设置了lazy-init属性，则容器在完成Bean定义的注册之后，会通过getBean方法，触发对指定Bean的初始化和依赖注入过程，这样当应用第一次向容器索取所需的Bean时，容器不再需要对Bean进行初始化和依赖注入，直接从已经完成实例化和依赖注入的Bean中取一个现成的Bean，这样就提高了第一次获取Bean的性能。</p>
</article><section class="jump-container is-flex is-justify-content-space-between my-6"><!-- em is empty placeholder--><a class="button is-default" href="/blog/2022/12/01/Spring/Spring%E7%9A%84Bean%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%AD%96%E7%95%A5InstantiationStrategy/" title="【Spring源码解析系列】 Bean实例化策略InstantiationStrategy"><i class="iconfont icon-prev mr-2 has-text-grey"></i><span class="has-text-weight-semibold">上一页: 【Spring源码解析系列】 Bean实例化策略InstantiationStrategy</span></a><a class="button is-default" href="/blog/2022/12/01/SpringBoot/0-SpringBoot-%E6%A8%A1%E6%9D%BF/" title="【SpringBoot源码解析系列】"><span class="has-text-weight-semibold">下一页: 【SpringBoot源码解析系列】</span><i class="iconfont icon-next ml-2 has-text-grey"></i></a></section><article class="mt-6 comment-container"><script async repo="janedler/blog" src="https://utteranc.es/client.js" issue-term="pathname" theme="preferred-color-scheme"></script></article></div></div></main></main><footer class="is-flex is-flex-direction-column is-align-items-center is-flex-shrink-0 is-family-serif"><section class="sns-container"><!-- Github--><a title="github" target="_blank" rel="noopener nofollow" href="//github.com//janedler"><i class="iconfont icon-github"></i></a><!-- Ins--><!-- RSS--><a title="rss" target="_blank" rel="noopener nofollow" href="/blog/atom.xml"><i class="iconfont icon-rss"></i></a><!-- 知乎--><!-- 领英--><!-- 脸书--></section><p><span>Copyright ©</span><span> 田园牧歌(*︶*) 2022</span></p><div class="is-flex is-justify-content-center is-flex-wrap-wrap"><p>Powered by Hexo &verbar;&nbsp;</p><p class="is-flex is-justify-content-center"><a title="Hexo theme author" target="_blank" rel="noopener" href="//github.com/haojen">Theme by Haojen&nbsp;</a></p><div style="margin-top: 2px"><a class="github-button" title="github-button" target="_blank" rel="noopener" href="https://github.com/haojen/hexo-theme-Claudia" data-color-scheme="no-preference: light; light: light; dark: dark;" data-show-count="true"></a></div></div><div><span></span></div></footer><script async defer src="https://buttons.github.io/buttons.js"></script><script src="/blog/js/post.js"></script></body></html>